/**************************************************************
 *
 * userprog/ksyscall.h
 *
 * Kernel interface for systemcalls 
 *
 * by Marcus Voelp  (c) Universitaet Karlsruhe
 *
 **************************************************************/

#ifndef __USERPROG_KSYSCALL_H__ 
#define __USERPROG_KSYSCALL_H__ 

#include "kernel.h"
#include "stdio.h"
#include "synchconsole.h"
#include "addrspace.h"
#include "syscall.h"
#include "filesys.h"
#include <string>

/**
 * The follow functions is used to copy data from or to user 
 *     program space.
 ** They are used when user program pass a point to a system call.
 *
 * CopyStrToUser
 *
 * CopyStrFromUser
 *
 * CopyToUser
 *
 * CopyFromUser
 *
 */
int CopyStrToUser( int addr, const char * buf )
{
    do {
        kernel->machine->WriteMem(addr++, 1, (int)(*buf));
    } while(*(buf++));

    return 0;
}

int CopyStrFromUser( int addr, char * buf, int buf_size )
{
    int read_cnt = 0;
    do {
        kernel->machine->ReadMem(addr++, 1, (int*)buf);
        if( ++read_cnt == buf_size )
        {
            *buf = '\0';
            return buf_size;
        }
    } while (*(buf++));
    
    return read_cnt;
}

int CopyToUser( int addr, void* buf, int size )
{
    char * p = (char*)buf;
    while( size-- )
    {
        kernel->machine->WriteMem(addr++, 1, (int)(*(p++)));
    }
    return size;
}

int CopyFromUser( int addr, void* buf, int size )
{
    char * p = (char*)buf;
    while( size-- )
    {
        kernel->machine->ReadMem(addr++, 1, (int*)(p++));
    }
    return size;
}

void SplitePath( char * szPath, char * szName )
{
    std::string szDirPath = string( szPath );
    size_t pos = szDirPath.find_last_of( "/\\" );

    if ( string::npos == pos ) {
        strcpy( szName, szPath );
        strcpy( szPath, "." );
    } else {
        strcpy( szName, szDirPath.substr(pos+1, szDirPath.size()).c_str() );
        szPath[pos] = '\0';
    }
}

/**
 * The following functions implement system call functions.
 */
 
void SysHalt() {
	kernel->interrupt->Halt();
}

int SysAdd(int op1, int op2) {
	return op1 + op2;
}

SpaceId SysExec(char * fileName) {

	char szFileName[256] = { 0 };
	CopyStrFromUser((int)fileName, szFileName, 256);
	DEBUG( dbgZwj, "System Call Exec: " << szFileName );

	kernel->currentThread->space->SaveRegister();
	AddrSpace * space = new AddrSpace(szFileName);
	if (space->Load()) {
		space->Execute(); // run the program
		ASSERTNOTREACHED(); // Execute never returns
	}

	return 1;
}

int SysExit(int exitCode) {
	AddrSpace * space = kernel->lstAddrSpace->RemoveFront();
	delete space;
	DEBUG( dbgZwj, "System Call Exit..." );
	if (kernel->lstAddrSpace->IsEmpty())
		SysHalt();

	space = kernel->lstAddrSpace->Front();
	kernel->currentThread->space = space;
	space->RestoreRegister();
	space->RestoreState();
	kernel->machine->Run();

	return exitCode;
}

int SysPrint(char * msg) {
	int IntValue = 0;
	int IntMem = (int) msg;
	do {
		kernel->machine->ReadMem(int(IntMem++), 1, &IntValue);
		if (0 == IntValue)
			break;
		kernel->synchConsoleOut->PutChar((char) IntValue);
	} while (true);

	return 0;
}

int SysPrintNum(int num) {
	char num_str[16], *str_ptr = num_str;
	sprintf(num_str, "%d", num);
	while (0 != *str_ptr) {
		kernel->synchConsoleOut->PutChar(*(str_ptr++));
	}
	return 0;
}

int SysWrite(OpenFileId fd, int Addr, int size) {
	int Tmp = -1;
	while(size-- ) {
		if (!kernel->machine->ReadMem(Addr++, 1, &Tmp)) {
			break;
		}
		if (fd == ConsoleOutputId) { // write to console
			kernel->synchConsoleOut->PutChar(Tmp);
		}
	}

	return 0;
}

int SysRead(OpenFileId fd, int destAddr, int size) {
	int Tmp = 0;
	if (fd == ConsoleInputId) { // read from console
		for (int i = 0; i < size; i++) {
			Tmp = (int) kernel->synchConsoleIn->GetChar();
			kernel->machine->WriteMem((destAddr + i), 1, Tmp);
		}
	}

	return 0;
}

int SysChDir( int pathAddr ){
    char szFilePath[64] = { 0 };
    CopyStrFromUser( pathAddr, szFilePath, 64 );

    if( kernel->fileSystem->ChDir(szFilePath) )
        return 0;

    return -1;
}

int SysGetCwd( int pathAddr, int size ){
    std::string szFilePath;
    kernel->fileSystem->GetCurWkDir( szFilePath );
    if( size <= szFilePath.size() ) 
        return -1;

    const char* p = szFilePath.c_str();
    CopyStrToUser( pathAddr, p );

    return 0;
}


int SysGetDent( int pathAddr,
                int dirEntAddr,
                int countAddr )
{
    int count;
    char szDirPath[64] = { 0 };
    CopyFromUser( countAddr, (void*)&count, 4 );
    CopyStrFromUser( pathAddr, szDirPath, 64 );

    DEBUG(dbgZwj, "SysGetDent: wanted count = " << count );
    DEBUG(dbgZwj, "SysGetDent: szDirPath = " << szDirPath );

    DirectoryEntry ents[10];
    bool ret = kernel->fileSystem->GetDirEnts( szDirPath, ents, count);
    DEBUG(dbgZwj, "SysGetDent: return " << ret << ", count = " << count );
    
    if( ret && count > 0 ){
        struct dirEnt * dirents = new struct dirEnt[count];
        for( int i = 0; i < count; i++ )    {
            dirents[i].isDir = ents[i].isDir;
            dirents[i].linkCnt = ents[i].linkCount;
            strcpy( dirents[i].entryName, ents[i].name);
            DEBUG(dbgZwj, "SysGetDent: item name = " << dirents[i].entryName );
        }

        CopyToUser(dirEntAddr, (void *)dirents, count*sizeof(struct dirEnt));
        CopyToUser(countAddr, (void *)&count, 4);
        delete []dirents;
    }
    
    CopyToUser(countAddr, (void *)&count, 4);
    if(ret)
        return 0;
    return -1;
}

int SysMkDir( int pathAddr ) {
    char szPath[64] = { 0 };
    char szName[10] = { 0 };
    CopyStrFromUser( pathAddr, szPath, 64 );

    SplitePath( szPath, szName );
    DEBUG(dbgZwj, "MkDir: path = " << szPath << ", name = " << szName );
    
    bool ret = kernel->fileSystem->MakeDir(szPath, szName);
    return ret? 0: -1;
}

int SysCreate( int pathAddr ) {
    char szPath[64] = { 0 };
    char szName[10] = { 0 };
    CopyStrFromUser( pathAddr, szPath, 64 );

    SplitePath( szPath, szName );
    DEBUG(dbgZwj, "Create: path = " << szPath << ", name = " << szName );
    
    bool ret = kernel->fileSystem->Create(szPath, szName);
    return ret? 0: -1;
}

int SysRmDir( int pathAddr, int resv ) {
    char szPath[64] = { 0 };
    char szName[10] = { 0 };
    CopyStrFromUser( pathAddr, szPath, 64 );

    SplitePath( szPath, szName );
    DEBUG(dbgZwj, "RmDir: path = " << szPath << ", name = " << szName );

    bool bResv = ( resv == 1 );
    int ret = kernel->fileSystem->Remove(szPath, szName, TRUE, bResv);
    return ret;
}

int SysRename( int pathAddr, int newnameAddr ) {
	char szPath[64] = { 0 };
	char szsName[10] = { 0 };
	char szdName[10] = { 0 };
	CopyStrFromUser( pathAddr, szPath, 64 );
	CopyStrFromUser( newnameAddr, szdName, 10 );

	SplitePath( szPath, szsName );
	DEBUG( dbgZwj, "Rename: path = " << szPath << ", old name = " << szsName << ", new name = "<< szdName );

	bool ret = kernel->fileSystem->Rename(szPath, szsName, szdName);
	return ret? 0: -1;
}

int SysRemove( int pathAddr ) {
	char szPath[64] = { 0 };
	char szName[10] = { 0 };
	CopyStrFromUser( pathAddr, szPath, 64 );

	SplitePath( szPath, szName );
	DEBUG( dbgZwj, "Remove: path = " << szPath << ", name = " << szName );

	int ret = kernel->fileSystem->Remove(szPath, szName, FALSE);
	return ret;
}

int SysCopy( int spathAddr, int dpathAddr ) {
	char szsPath[64] = { 0 };
	char szsName[10] = { 0 };
	char szdPath[64] = { 0 };
	char szdName[10] = { 0 };
	CopyStrFromUser( spathAddr, szsPath, 64 );
	CopyStrFromUser( dpathAddr, szdPath, 64 );

	SplitePath( szsPath, szsName );
	SplitePath( szdPath, szdName );
	DEBUG( dbgZwj, "copy: source path = " << szsPath << ", source name = " << szsName << ", destination path = " << szdPath << ", destination name = " << szdName );
	
	bool ret = kernel->fileSystem->Copy( szsPath, szsName, szdPath, szdName );
	return ret? 0: -1;  
}

int SysAppend( int spathAddr, int dpathAddr ) {
	char szsPath[64] = { 0 };
	char szsName[10] = { 0 };
	char szdPath[64] = { 0 };
	char szdName[10] = { 0 };
	CopyStrFromUser( spathAddr, szsPath, 64 );
	CopyStrFromUser( dpathAddr, szdPath, 64 );

	SplitePath( szsPath, szsName );
	SplitePath( szdPath, szdName );
	DEBUG( dbgZwj, "append: source path = " << szsPath << ", source name = " << szsName << ", destination path = " << szdPath << ", destination name = " << szdName );
	
	bool ret = kernel->fileSystem->Append( szsPath, szsName, szdPath, szdName );
	return ret? 0: -1;  
} 

int SysList( int pathAddr, bool resv ) {
    char szPath[64] = { 0 };
    CopyStrFromUser(pathAddr, szPath, 64);
    
    DEBUG( dbgZwj, "SysList: path = " << szPath << ", resv = " << resv );
    std::string szDirPath = szPath;
    bool ret = kernel->fileSystem->List( szDirPath, resv );
    return ret? 0: -1;
}

int SysCat( int pathAddr ) {
	char szPath[64] = { 0 };
	char szName[10] = { 0 };
	CopyStrFromUser( pathAddr, szPath, 64 );
    SplitePath( szPath, szName );

    bool ret = kernel->fileSystem->CatFile( szPath, szName );
    return ret? 0: -1;
}

int SysGetFile( int fromFileAddr, int toFileAddr ) {
	char fromPath[64] = { 0 };
	char toPath[64] = { 0 };
	char toName[10] = { 0 };
	CopyStrFromUser( fromFileAddr, fromPath, 64 );
	CopyStrFromUser( toFileAddr, toPath, 64 );

	SplitePath( toPath, toName );
	DEBUG( dbgZwj, "GetFile: source path = " << fromPath 
                 << ", destination path = " << toPath << ", destination name = " << toName );
	
	bool ret = kernel->fileSystem->GetFile( fromPath, toPath, toName );
	return ret? 0: -1;  
} 

#endif /* ! __USERPROG_KSYSCALL_H__ */
